#pragma once

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/timestamp.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/audio_fifo.h>
#include <libavutil/avassert.h>
#include <libavutil/avstring.h>
#include <libavutil/dict.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
}

#include <opencv2/opencv.hpp>
#include "common/base_struct.h"
#include "stream/total_struct.h"
#include "base_util/log_lite.h"

namespace stream {


struct DecoderData {
  int thread_id;
  int decode_id;
  int size_y;
  int size_uv;
  std::string pkt_id;
  AVCodecID codec_id;
  AVPacket* pkt;
  std::vector<char>* yuv;
  int source_id;
  int width;
  int height;
};

class BasePuller
{
public:
  BasePuller(StreamPullerInfo* sifo, LogInfo* log_info):
    puller_ifo(sifo), log_ifo(log_info), stop_flag(common::CameraClosed),
    stop_flag2(common::CameraClosed) {
      encode_type = puller_ifo->enType;
      pull_mode = -1;
      pull_mode_value = -1;
      buffer_cnt = 1;
    }
  virtual ~BasePuller() {}

public:
  virtual int init_params() = 0;
  virtual int start_pull() = 0;
  virtual int stop_pull() = 0;

  virtual int get_frame(stream::ImageBlob& buffer) = 0;

  void set_mode(int strategy_mode, float strategy_value, int buffer_size) {
    pull_mode = strategy_mode;
    pull_mode_value = strategy_value;
    buffer_cnt = buffer_size;
    // spdlog::get("logger")->info("BasePuller::set_mode. pull_mode:{}, pull_mode_value:{}, buffer_cnt:{}", pull_mode, pull_mode_value, buffer_cnt);
  }

public:
  LogInfo* log_ifo;
  StreamPullerInfo* puller_ifo;
  int encode_type;
  int pull_mode;
  float pull_mode_value;
  int buffer_cnt;

  std::atomic_int stop_flag;
  std::atomic_int stop_flag2;


};

class BasePktDecoder;
class BaseDecoderManger
{
public:
  BaseDecoderManger(LogInfo* log_info);
  virtual ~BaseDecoderManger();

public:
  virtual int init_decode() = 0;
  
  int set_data(int& set_idx, DecoderData* ddata);

  int get_data(int set_idx);

  int release_data(int source_id);

public:
  LogInfo *log_ifo;
  std::vector<BasePktDecoder*> decoders;
  std::map<int, int> decode_thread_map;

  std::vector<DecoderData*> in_datas;
	std::mutex in_data_mtx;
  // 维护 in_datas 的全局索引
  int in_loop_idx;
  int set_data_idx = 0;
	std::mutex out_data_mtx;
  std::map<int, int> out_datas;

public:
  std::atomic_int stop_flag1;
};



class BasePktDecoder
{
public:
  BasePktDecoder(BaseDecoderManger* decode_mnger, int dw, int dh, int thread_id, int gpu_id, LogInfo* log_info):
  decode_mng(decode_mnger), decode_width(dw), decode_height(dh), thread_id(thread_id), gpu_id(gpu_id), log_ifo(log_info) {
    decode_data = nullptr;
    cur_fps = 1;
    decode_chls = 0;
  }
  virtual ~BasePktDecoder() {}

public:
  virtual int init_params() = 0;
  virtual int run_thread() = 0;
  virtual int stop_decode() = 0;
  virtual int release_params() = 0;


public:
  LogInfo *log_ifo;
  std::atomic_int stop_flag;
  BaseDecoderManger* decode_mng;
  DecoderData* decode_data;
  int thread_id;
  int decode_chls;
  int gpu_id; 
  int decode_width;
  int decode_height;

  AVFormatContext *ifmt_ctx = nullptr;
  AVDictionary *optionsDict = nullptr;
  AVStream *pVst = nullptr;
  AVCodecContext *pVideoCodecCtxH264 = nullptr;
  AVCodecContext *pVideoCodecCtxH265 = nullptr;
  AVCodec *pVideoCodecH264 = nullptr;
  AVCodec *pVideoCodecH265 = nullptr;
  AVFrame *pFrame = nullptr;
  int video_st_index = -1;
  int audio_st_index = -1;
  float cur_fps;

  // init module
  // void *display = nullptr;
  // void *vps = nullptr;

  std::string rtsp_main;
  int input_batch;

};


}   // namespace stream
